/*
  fstags - manage tags for your files
  Copyright (C) 2007  Victor Bogado and Rodolfo Borges

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation; either version 2
  of the License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/syscall.h>

int fstags_update( const char* from, const char* to )
{
	char* verbose = getenv("FSTAGS_VERBOSE");
	char* dbg = getenv("FSTAGS_DEBUG");
	if(dbg) printf("fstags_update(%s, %s)\n", from, to);

	// get inode of the original file
	int inode = -1;
	struct stat st;
	if( stat( from, &st ) == 0 )
		inode = st.st_ino;

	// execute the system call
	int rc;
	if( to == NULL )
		rc = syscall( SYS_unlink, from );
	else
		rc = syscall( SYS_rename, from, to );
	if( rc != 0 )
		return rc;

	// build the full pathname of the ~/.tags dir
	char tagsdir[FILENAME_MAX];
	char* homedir = getenv("HOME");
	if( homedir == NULL )
		return 0;
	if( snprintf( tagsdir, FILENAME_MAX, "%s/.tags", homedir ) >= FILENAME_MAX )
		return 0; // too big
	if(dbg) printf("tagsdir(%s)\n", tagsdir);

	// build basename of the tag entry we'll be looking for
	static char tag_entry[21]; // 20 = max digits of a 64-bit integer
	sprintf( tag_entry, "%d", inode );

	// read the ~/.tags dir
	DIR* dir = opendir( tagsdir );
	if( dir == NULL ) {
		if(verbose||dbg) perror(tagsdir);
		return 0;
	}
	struct dirent* dirent;
	while( (dirent = readdir(dir)) != NULL ) {

		// ignore entries starting with a dot
		if( dirent->d_name[0] == '.' )
			continue;

		// build full pathname of tag entry
		char tagdir[FILENAME_MAX];
		if( snprintf( tagdir, FILENAME_MAX, "%s/%s", tagsdir, dirent->d_name ) >= FILENAME_MAX )
			continue; // too big
		if(dbg) printf("tagdir(%s)\n", tagdir);

		// check if entry is a directory
		struct stat st;
		if( stat( tagdir, &st ) != 0 )
			continue;
		if( !S_ISDIR(st.st_mode) )
			continue;

		// read ~/.tags/TAG dir
		DIR* dir = opendir( tagdir );
		if( dir == NULL ) {
			if(verbose||dbg) perror(tagdir);
			continue;
		}
		int entries = 0;
		while( (dirent = readdir(dir)) != NULL ) {
			++entries;

			if( strcmp( dirent->d_name, tag_entry ) != 0 )
				continue;

			char link[FILENAME_MAX];
			if( snprintf( link, FILENAME_MAX, "%s/%s", tagdir, tag_entry ) >= FILENAME_MAX )
				continue; // too big

			// check if entry is a symlink
			if( lstat( link, &st ) != 0 )
				continue;
			if( !S_ISLNK(st.st_mode) )
				continue;

			if(verbose||dbg) printf("removing %s\n", link);
			if( syscall( SYS_unlink, link ) != 0 ) {
				if(verbose||dbg) perror(link);
				continue;
			}

			if( to == NULL ) {
				--entries;
			}
			else {
				char target[FILENAME_MAX];
				if( to[0] == '/' ) {
					strncpy( target, to, FILENAME_MAX );
				}
				else {
					char* pwd = getcwd(0,0);
					if( pwd == NULL )
						continue;
					if( snprintf( target, FILENAME_MAX, "%s/%s", pwd, to ) >= FILENAME_MAX ) {
						free(pwd);
						continue; // too big
					}
					free(pwd);
				}
				if(verbose||dbg) printf("creating link from %s to %s\n", target, link);
				symlink( target, link );
			}
		}
		closedir(dir);
		
		// remove empty tag dir
		if( entries == 2 ) { // . and ..
			if(verbose||dbg) printf("removing empty dir %s\n", tagdir);
			if( rmdir( tagdir ) != 0 )
				if(verbose||dbg) perror(tagdir);
		}
	}

	closedir(dir);

	return 0;
}

int rename( const char* from, const char* to )
{
	return fstags_update( from, to );
}

int unlink( const char* filename )
{
	return fstags_update( filename, NULL );
}

// vim600:fdm=syntax:fdn=1:
